tree grammar LocScriptCheckPass;

options {
	tokenVocab = LocScript;
	ASTLabelType = LocScriptAST;
	filter = true;
}

@header {
	package script.grammar;
	
	import script.grammar.symbol.*;
}

@members {
	SymbolTable symbolTable;
	Scope currentScope;
	
	public LocScriptCheckPass(TreeNodeStream in, SymbolTable symbolTable) {
		this(in);
		this.symbolTable = symbolTable;
		currentScope = symbolTable.getLocalScope();
	}
}

topdown
	:	enterFor
	|	enterBlock
	|	atoms
	;

bottomup
	:	exitBlock
	|	exprRoot
	|	returnStat
	|	def
	|	foreach
	;
	
enterFor
	:	FOR {currentScope = new BaseScope("Foreach Scope", currentScope);}
	;
	
enterBlock
	:	BLOCK {currentScope = new BaseScope("Block Scope", currentScope);}
	;
	
exitBlock
	:	BLOCK {currentScope = currentScope.getEnclosingScope();}
	;
	
atoms
@init {LocScriptAST t = (LocScriptAST)input.LT(1);}
    :  {t.hasAncestor(EXPR)||t.hasAncestor(DEF)}? ID
       {t.scope = currentScope;}
    ;
	
foreach
	:	^(FOR v=. e=. .) {currentScope = currentScope.getEnclosingScope(); symbolTable.handleForEach($v, $e);}
	;
	
returnStat
	:	^(RETURN a=.) {symbolTable.handleReturn($a);}
	;
	
def
@init {LocScriptAST init = null;}
	:	^(DEF a=ID b=ID (exprRoot {init=$exprRoot.start;})?)  {symbolTable.defineVariable($a, $b, init);}
	;
	
exprRoot
	:	^(EXPR expr) {$EXPR.evalType = $expr.type;}
	;
	
expr returns [Type type]
@after {$start.evalType=$type;}
	:	(TRUE|FALSE) {$type=NativeType.BOOLEAN;}
	|	INT {$type=NativeType.INT;}
	|	FLOAT {$type=NativeType.DOUBLE;}
	|	^(VAR ID)
		{
		Symbol s = symbolTable.resolveVariable($ID);
		$type = s.getType();
		}
	|	member {$type=$member.type;}
	|	unaryOp {$type=$unaryOp.type;}
	|	binaryOp {$type=$binaryOp.type;}
	|	ternaryOp {$type=$ternaryOp.type;}
	|	arrayAccess {$type=$arrayAccess.type;}
	;
	
member returns [Type type]
	:	^(MEM_ACCESS THIS ID) 
		{$type = symbolTable.resolveMember($ID).getType();}
	|	^(MEM_ACCESS expr ID) 
		{$type = symbolTable.resolveMember($expr.start, $ID).getType();}
	;
	
unaryOp returns [Type type]
	:	^(o=(NEG|BIT_NEG|NOT|PRE_INC|PRE_DEC|POST_INC|POST_DEC) expr)
		{$type=symbolTable.computeType($o, $expr.start);}
	;
	
binaryOp returns [Type type]
	:	^(o=(PLUS|MINUS|MULT|DIV|MOD|
			GREATER_THAN|LESS_THAN|GREATER_THAN_EQUALS|LESS_THAN_EQUALS|
			EQUALS|NOT_EQUALS|
			AND|OR|
			BIT_AND|BIT_OR|XOR|
			SHIFT_LEFT|SHIFT_RIGHT|U_SHIFT_RIGHT|
			ASSIGN|PLUS_ASSIGN|MINUS_ASSIGN|MULT_ASSIGN|DIV_ASSIGN) 
			a=expr b=expr)
		{$type=symbolTable.computeType($o, $a.start, $b.start);}
	;
	
ternaryOp returns [Type type]
	:	^(COND_OP a=expr b=expr c=expr) {$type=symbolTable.computeType($COND_OP, $a.start, $b.start, $c.start);}
	;
	
arrayAccess returns [Type type]
	: ^(ARRAY_ACCESS a=expr b=expr) {$type=symbolTable.handleArrayAccess($a.start, $b.start);}
	;